﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Diagnostics;
using System.Globalization;
using System.Linq;
using System.Reflection;
using System.Windows;

namespace RapidDownloader.Modules.Views.Diagnostics
{
    /// <summary>
    /// Interaction logic for BindingsOverview.xaml
    /// </summary>
    public partial class BindingsOverview
    {
        #region Fields

        public static readonly DependencyProperty ReflectPropertiesProperty = DependencyProperty.Register("ReflectProperties", typeof(ReadOnlyCollection<ReflectPropertyDescriptorInfo>), typeof(BindingsOverview));

        #endregion

        #region Constructors

        public BindingsOverview()
        {
            InitializeComponent();
            DataContext = this;

            Refresh();
        }

        #endregion

        #region Properties

        public ReadOnlyCollection<ReflectPropertyDescriptorInfo> ReflectProperties
        {
            get { return (ReadOnlyCollection<ReflectPropertyDescriptorInfo>)GetValue(ReflectPropertiesProperty); }
            set { SetValue(ReflectPropertiesProperty, value); }
        }

        #endregion

        #region Private Methods

        private void RefreshButtonClicked(object sender, RoutedEventArgs e)
        {
            Refresh();
        }

        private void Refresh()
        {
            ReflectProperties = GetReflectPropertyDescriptorInfo();
        }

        #endregion

        #region Class Methods

        private static ReadOnlyCollection<ReflectPropertyDescriptorInfo> GetReflectPropertyDescriptorInfo()
        {
            var listInfo = new List<ReflectPropertyDescriptorInfo>();

            var typeRtdp = typeof(PropertyDescriptor).Module.GetType("System.ComponentModel.ReflectTypeDescriptionProvider");
            var propertyCacheFieldInfo = typeRtdp.GetField("_propertyCache", BindingFlags.Static | BindingFlags.NonPublic);
            Debug.Assert(propertyCacheFieldInfo != null);

            var propertyCache = (Hashtable)propertyCacheFieldInfo.GetValue(null);

            if (propertyCache != null)
            {
                var entries = new DictionaryEntry[propertyCache.Count];
                propertyCache.CopyTo(entries, 0);

                var valueChangedHandlersFieldInfo = typeof(PropertyDescriptor).GetField("valueChangedHandlers", BindingFlags.Instance | BindingFlags.NonPublic);
                Debug.Assert(valueChangedHandlersFieldInfo != null);

                // count the "value changed" handlers for each type
                listInfo.AddRange(from entry in entries
                                  let pds = (PropertyDescriptor[])entry.Value
                                  where pds != null
                                  from pd in pds
                                  let valueChangedHandlers = (Hashtable)valueChangedHandlersFieldInfo.GetValue(pd)
                                  where valueChangedHandlers != null && valueChangedHandlers.Count != 0
                                  select new ReflectPropertyDescriptorInfo(entry.Key.ToString(), pd.Name, valueChangedHandlers.Count));
            }

            listInfo.Sort();
            return listInfo.AsReadOnly();
        }

        #endregion
    }

    public sealed class ReflectPropertyDescriptorInfo : IEquatable<ReflectPropertyDescriptorInfo>, IComparable<ReflectPropertyDescriptorInfo>
    {
        #region Constructors

        public ReflectPropertyDescriptorInfo(string typeName, string propertyName, int handlerCount)
        {
            TypeName = typeName;
            PropertyName = propertyName;
            HandlerCount = handlerCount;
        }

        #endregion

        #region Properties

        public string TypeName { get; private set; }

        public string PropertyName { get; private set; }

        public int HandlerCount { get; private set; }

        public string DisplayHandlerCount
        {
            get
            {
                return HandlerCount == 1 ? string.Empty : string.Format(CultureInfo.InvariantCulture, " ({0:n0} handlers)", HandlerCount);
            }
        }

        #endregion

        #region Public Methods

        public int CompareTo(ReflectPropertyDescriptorInfo other)
        {
            if (ReferenceEquals(other, null))
                return 1;

            var compareResult = TypeName.CompareTo(other.TypeName);
            if (compareResult == 0)
                compareResult = PropertyName.CompareTo(other.PropertyName);
            if (compareResult == 0)
                compareResult = HandlerCount.CompareTo(other.HandlerCount);
            return compareResult;
        }

        public override int GetHashCode()
        {
            return TypeName.GetHashCode() << 24 ^ PropertyName.GetHashCode() << 8 ^ HandlerCount;
        }

        public override bool Equals(object obj)
        {
            return Equals(obj as ReflectPropertyDescriptorInfo);
        }

        public bool Equals(ReflectPropertyDescriptorInfo other)
        {
            return other != null && other.TypeName == TypeName && other.PropertyName == PropertyName && other.HandlerCount == HandlerCount;
        }

        #endregion
    }
}